#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include "rbtree.h"
#include "public_relate.h"
#include "macro.h"

/* root of tasks */
struct rb_root rbroot = RB_ROOT;

static int get_pid_exe(int size, char *buf, int pid)
{
	int i;
	char file[RELEN];
	snprintf(file, RELEN, "/proc/%d/exe", pid);
	i = readlink(file, buf, RELEN);
	if ( i < 0 )
		return -1;
	buf[i] = 0;
	return 0;
}

/* find task from rb_tree with pid and return it, if not
   found ,return NULL
   */
struct task_struct *find_task_by_vpid(int pid)
{
    struct rb_node *node = rbroot.rb_node;
    int result;
  	while (node) {
  		struct task_struct *t= container_of(node, struct task_struct, node);
		result = pid - t->pid;
		if (result < 0)
  			node = node->rb_left;
		else if (result > 0)
  			node = node->rb_right;
		else
  			return t;
	}
	return NULL;
}

static int my_insert(struct rb_root *root, struct task_struct *data)
{
    struct rb_node **new = &(root->rb_node), *parent = NULL;

    /* Figure out where to put new node */
    while (*new) {
        struct task_struct *this = container_of(*new, struct task_struct, node);
        int result = data->pid - this->pid;

        parent = *new;
        if (result < 0)
            new = &((*new)->rb_left);
        else if (result > 0)
            new = &((*new)->rb_right);
        else
            return -1;
}

  	/* Add new node and rebalance tree. */
  	rb_link_node(&data->node, parent, new);
  	rb_insert_color(&data->node, root);

	return 0;
}

/* create a task with pid, and insert to rb_tree */
struct task_struct *create_task(int pid)
{
    int ret;
    struct task_struct *p = malloc(sizeof(*p));
    memset(p, 0, sizeof(*p));
    p->pid = pid;
    p->usage= 0;
    if ( ( ret = get_pid_exe(RELEN, p->name, pid)) < 0)
        strcpy(p->name, "none");
    RDprint("\t\tmalloc a task, pid:%d name:%s\n", p->pid, p->name);
    ret = my_insert(&rbroot, p);
#ifdef R_DEBUG
    if ( ret < 0)
        RDprint("\t\t\tinsert error, find same pid: %d task in rbtree!\n", p->pid);
#endif
    return p;
}

/* free a task, and remove from rb_tree */
void task_free(struct task_struct *p)
{
    rb_erase(&p->node, &rbroot);
    free(p);
}

/*show rbtree */
void show_rbtree(FILE *fp)
{
    struct rb_node *node;
    struct task_struct *p;
    for (node = rb_first(&rbroot); node; node = rb_next(node))
    {
        p = container_of(node, struct task_struct, node);
        fprintf(fp, "RBTREE: pid:%d name:%s\n", p->pid, p->name);
    }
}

static int is_exist(int pid)
{
    struct stat st;
    char path[64];
    int i;
    int old_errno = errno;
    snprintf(path, 64, "/proc/%d", pid);
    i = stat(path, &st);
    errno = old_errno;
    return i<0 ? 0 : 1;
}

void validate_all_pids(FILE *fp)
{   
    struct rb_node *node;
    struct task_struct *p;
    for (node = rb_first(&rbroot); node; node = rb_next(node))
    {
        p = container_of(node, struct task_struct, node);
//            fprintf(fp, "volidate: %s %d\n", p->name, p->pid);
        if (!is_exist(p->pid))
        {
			if ( fp != NULL) /* for debug */
           		fprintf(fp, "process not exist %s pid:%d\n", p->name, p->pid);
            exit_relate(p->pid);
        }
    }
}
